<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>AI 旅行规划师</title>

    <script type="text/javascript">
      window._AMapSecurityConfig = {
        securityJsCode: '2e3665ecfee9e2b59af5c5da3ee1f1ee', 
      }
    </script>
    <script type="text/javascript" 
      src="https://webapi.amap.com/maps?v=2.0&key=7ed048f1d5d073d4a5b465488264a1f6">
    </script>
    
    <style>
        /* (你的 style 样式表保持不变, 已折叠) */
        body { font-family: sans-serif; max-width: 800px; margin: auto; padding: 20px; }
        textarea { width: 100%; height: 80px; box-sizing: border-box; }
        #result-wrapper { border: 1px solid #ccc; padding: 10px; min-height: 100px; }
        #itinerary-content { background: #fdfdfd; padding: 10px; border-radius: 5px; }
        #itinerary-content h4 { border-bottom: 1px solid #ddd; padding-bottom: 5px; }
        #itinerary-content ul { padding-left: 20px; }
        #auth-section, #plan-section { margin-bottom: 20px; padding: 10px; background: #eee; }
        #saved-plans-list { list-style: none; padding: 0; }
        
        /* ⬇️ saved-plans-list 样式 ⬇️ */
        #saved-plans-list li { 
            background: #fff; border: 1px solid #ddd; padding: 10px; 
            margin-bottom: 5px;
            display: flex; /* ⬅️ 新增 */
            justify-content: space-between; /* ⬅️ 新增 */
            align-items: center; /* ⬅️ 新增 */
        }
        #saved-plans-list li:hover { background: #f0f0f0; }
        #saved-plans-list .plan-item-content { flex-grow: 1; cursor: pointer; } /* ⬅️ 新增 */
        #saved-plans-list .plan-item-content strong { display: block; margin-bottom: 5px; font-size: 1.1em; }
        #saved-plans-list .plan-item-content small { color: #555; }
        /* ⬆️ saved-plans-list 样式 ⬆️ */

        .hidden { display: none; }
        #record-btn { background: #f44336; color: white; border: none; padding: 10px; border-radius: 50%; cursor: pointer; width: 40px; height: 40px; margin-left: 10px; }
        #record-btn.recording { background: #b71c1c; animation: pulse 1.5s infinite; }
        @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(244, 67, 54, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(244, 67, 54, 0); } 100% { box-shadow: 0 0 0 0 rgba(244, 67, 54, 0); } }
        #expense-section { margin-top: 20px; padding-top: 10px; border-top: 2px solid #ccc; }
        #expense-comparison { display: flex; justify-content: space-between; gap: 20px; }
        #ai-budget-section, #my-expenses-section { flex: 1; }
        #expense-form input { margin-right: 5px; width: 100px;}
        #expenses-list, #ai-budget-list { list-style: none; padding: 0; }
        #expenses-list li, #ai-budget-list li { display: flex; justify-content: space-between; padding: 5px; border-bottom: 1px solid #eee; }
        #ai-budget-list li small { color: #777; font-size: 0.9em; }
        #budget-summary { margin-top: 20px; padding-top: 10px; border-top: 1px solid #aaa; }
        #budget-summary h3 { color: #f44336; }
        #map-container { width: 100%; height: 400px; margin-top: 20px; border: 1px solid #ccc; }
        .amap-info-content { padding: 0; }
        .amap-info-outer { border-radius: 5px; }
        .info-window-content { padding: 10px; font-size: 14px; background: #fff; max-width: 300px; }
        .info-window-content img { width: 100%; height: 120px; object-fit: cover; border-radius: 3px; border: 1px solid #eee; }
        .info-window-content hr { border:none; border-top:1px solid #eee; margin: 5px 0; }
        .custom-label { background: #fff; border: 2px solid; padding: 3px 7px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); font-size: 14px; font-weight: bold; white-space: nowrap; }
        .custom-marker-icon { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; background: #1D2088; color: #fff; font-size: 14px; font-weight: bold; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 2px 5px rgba(0,0,0,0.3); }
    </style>
</head>
<body>

    <div id="auth-section"><h3>用户中心</h3><div id="login-form"><input type="email" id="email" placeholder="邮箱" /><input type="password" id="password" placeholder="密码" /><button id="register-btn">注册</button><button id="login-btn">登录</button></div><div id="user-info" class="hidden">欢迎, <span id="user-email"></span>!<button id="logout-btn">退出登录</button></div></div>
    <div id="plan-section"><h3>AI 智能行程规划</h3><textarea id="prompt-input" placeholder="例如：我想去上海，5天，预算1万元..."></textarea><div style="display: flex; align-items: center; margin-top: 10px;"><button id="generate-btn">🤖 点我生成行程</button><button id="record-btn" title="按住说话">🎤</button><span id="record-status" style="margin-left: 10px;"></span></div>
        <div id="result-wrapper" style="margin-top: 20px;">
            <h3 id="plan-title-display"></h3> 
            <div id="map-container">地图加载中...</div> <h4>行程详情:</h4>
            <div id="itinerary-content">这里会显示 AI 的行程...</div>
            <button id="save-plan-btn" class="hidden">💾 保存此行程</button>
            <div id="expense-section" class="hidden"><div id="expense-comparison"><div id="ai-budget-section"><h4>🤖 AI 预算估算</h4><ul id="ai-budget-list"></ul><h4>AI 估算总计: <span id="ai-budget-total">0</span> 元</h4></div><div id="my-expenses-section"><h4>✍️ 我的实际开销</h4><div id="expense-form"><input type="text" id="expense-item" placeholder="项目"><input type="number" id="expense-amount" placeholder="金额"><button id="add-expense-btn">记一笔</button></div><ul id="expenses-list"></ul><h4>我的实际总计: <span id="expense-total">0</span> 元</h4></div></div><div id="budget-summary"><h3>📊 预算剩余: <span id="budget-remaining">0</span> 元</h3></div></div>
        </div>
    </div>
    <div id="saved-plans-section" class="hidden"><h3>我保存的行程</h3><ul id="saved-plans-list"></ul></div>

    <script src="/static/js/supabase.min.js?v=2" defer></script> 
    
    <script>
        document.addEventListener('DOMContentLoaded', (event) => {
            
            // 2. Supabase Key (已填入)
            const SUPABASE_URL = 'https://zgqsmplkioucucoffcei.supabase.co';
            const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpncXNtcGxraW91Y3Vjb2ZmY2VpIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjI3ODc0MTAsImV4cCI6MjA3ODM2MzQxMH0.-2yo1s28G-ptiUTLoZQjDYHl-8s2HQUOzZzah30Lljw';
            const supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_KEY);
            
            // (全局变量, 已折叠)
            let currentUser = null; let currentPlanId = null; let currentPlanData = null; 
            let currentManualTotal = 0; let currentAiTotal = 0;
            let map = null; 
            let allMarkers = []; 
            let allPolylines = []; 

            // (获取页面元素, 已折叠)
            const loginForm = document.getElementById('login-form'); const userInfo = document.getElementById('user-info'); const userEmail = document.getElementById('user-email'); const logoutBtn = document.getElementById('logout-btn'); const registerBtn = document.getElementById('register-btn'); const loginBtn = document.getElementById('login-btn'); const savePlanBtn = document.getElementById('save-plan-btn'); const savedPlansSection = document.getElementById('saved-plans-section'); const savedPlansList = document.getElementById('saved-plans-list'); const planTitleDisplay = document.getElementById('plan-title-display'); const itineraryContent = document.getElementById('itinerary-content'); const promptInput = document.getElementById('prompt-input'); const recordBtn = document.getElementById('record-btn'); const recordStatus = document.getElementById('record-status'); const expenseSection = document.getElementById('expense-section'); const myExpensesSection = document.getElementById('my-expenses-section'); const expenseItemInput = document.getElementById('expense-item'); const expenseAmountInput = document.getElementById('expense-amount'); const addExpenseBtn = document.getElementById('add-expense-btn'); const expensesList = document.getElementById('expenses-list'); const expenseTotal = document.getElementById('expense-total'); const aiBudgetList = document.getElementById('ai-budget-list'); const aiBudgetTotal = document.getElementById('ai-budget-total'); const budgetRemaining = document.getElementById('budget-remaining');
            const mapContainer = document.getElementById('map-container');

            // (阶段 3, 4, 5 的 JS 代码保持不变, 已折叠)
            (async () => { const { data: { session } } = await supabaseClient.auth.getSession(); handleAuthState(session); })();
            supabaseClient.auth.onAuthStateChange((_event, session) => { handleAuthState(session); });
            function handleAuthState(session) { if (session && session.user) { currentUser = session.user; loginForm.classList.add('hidden'); userInfo.classList.remove('hidden'); userEmail.innerText = session.user.email; savedPlansSection.classList.remove('hidden'); loadSavedPlans(session.user.id); } else { currentUser = null; loginForm.classList.remove('hidden'); userInfo.classList.add('hidden'); savePlanBtn.classList.add('hidden'); savedPlansSection.classList.add('hidden'); savedPlansList.innerHTML = ""; expenseSection.classList.add('hidden'); if(mapContainer) {mapContainer.classList.add('hidden');} } }
            registerBtn.addEventListener('click', async () => { /* (无修改) */ const email = document.getElementById('email').value; const password = document.getElementById('password').value; const { data, error } = await supabaseClient.auth.signUp({ email: email, password: password }); if (error) { alert('注册失败: ' + error.message); } else { alert('注册成功! Supabase 默认需要邮箱验证，但我们已为你自动登录。'); console.log('注册成功!', data); } });
            loginBtn.addEventListener('click', async () => { /* (无修改) */ const email = document.getElementById('email').value; const password = document.getElementById('password').value; const { data, error } = await supabaseClient.auth.signInWithPassword({ email: email, password: password }); if (error) { alert('登录失败: ' + error.message); } else { console.log('登录成功!', data); } });
            logoutBtn.addEventListener('click', async () => { /* (无修改) */ const { error } = await supabaseClient.auth.signOut(); if (error) { alert('退出失败: ' + error.message); } });
            savePlanBtn.addEventListener('click', async () => { /* (无修改) */ if (!currentUser || !currentPlanData) return; try { const { data, error } = await supabaseClient.from('plans').insert({ userId: currentUser.id, plan_data: currentPlanData }).select(); if (error) throw error; const newPlan = data[0]; currentPlanId = newPlan.id; myExpensesSection.classList.remove('hidden'); loadExpenses(currentPlanId); alert('保存成功!'); loadSavedPlans(currentUser.id); } catch (error) { alert('保存失败: ' + error.message); } });
            
            // ⬇️ ⬇️ ⬇️ 【【【【 核心：加载“我的行程”列表 】】】】 ⬇️ ⬇️ ⬇️
            async function loadSavedPlans(userId) { 
                savedPlansList.innerHTML = "正在加载..."; 
                const { data: plans, error } = await supabaseClient.from('plans').select('id, plan_data, created_at').eq('userId', userId).order('created_at', { ascending: false }); 
                if (error) { alert('加载行程失败: ' + error.message); savedPlansList.innerHTML = "<li>加载失败...</li>"; return; } 
                savedPlansList.innerHTML = ""; 
                if (!plans || plans.length === 0) { savedPlansList.innerHTML = "<li>你还没有保存任何行程。</li>"; return; } 
                
                plans.forEach(plan => {
                    const li = document.createElement('li'); 
                    const planData = plan.plan_data;
                    const planId = plan.id; // ⬅️ 获取 Plan ID
                    if (!planData) return; 

                    const titleText = planData.plan_title || "未命名行程"; 
                    let snippetText = "暂无活动"; 
                    if(planData.itinerary && planData.itinerary[0] && planData.itinerary[0].activities[0]) { 
                        snippetText = planData.itinerary[0].activities[0].description.substring(0, 100) + "..."; 
                    }
                    
                    // 1. 创建内容 Div
                    const contentDiv = document.createElement('div');
                    contentDiv.className = 'plan-item-content'; // ⬅️ (应用样式)
                    contentDiv.innerHTML = `<strong>${titleText}</strong><small>${snippetText}</small>`;
                    
                    // 2. 创建删除按钮
                    const deleteBtn = document.createElement('button');
                    deleteBtn.innerText = '删除';
                    deleteBtn.style.marginLeft = '10px';
                    deleteBtn.style.backgroundColor = '#E60012'; // 红色
                    deleteBtn.style.color = 'white';
                    deleteBtn.style.border = 'none';
                    deleteBtn.style.borderRadius = '4px';
                    deleteBtn.style.padding = '5px 8px';
                    deleteBtn.style.cursor = 'pointer';

                    // 3. 把新元素“组装”到 li 中
                    li.appendChild(contentDiv);
                    li.appendChild(deleteBtn);
                    
                    // 4. 为“内容Div”绑定“加载”事件
                    contentDiv.addEventListener('click', () => { 
                        currentPlanData = plan.plan_data; 
                        currentPlanId = plan.id; 
                        expenseSection.classList.remove('hidden'); 
                        myExpensesSection.classList.remove('hidden'); 
                        savePlanBtn.classList.remove('hidden'); 
                        displayPlan(currentPlanData); 
                        loadExpenses(currentPlanId); 
                        window.scrollTo(0, 0); 
                    }); 
                    
                    // 5. 【【【 核心新功能：为“删除按钮”绑定“删除”事件 】】】
                    deleteBtn.addEventListener('click', async () => {
                        // 5a. 确认
                        if (!confirm(`确定要永久删除 "${titleText}" 吗？\n【警告】此操作将【同时删除】所有关联的开销记录，且无法撤销！`)) {
                            return;
                        }
                        
                        try {
                            // 5b. 【【 关键：先删“儿子”(Expenses) 】】
                            console.log(`正在删除 Plan ID: ${planId} 的所有开销...`);
                            const { error: expenseError } = await supabaseClient
                                .from('expenses')
                                .delete()
                                .eq('plan_id', planId);
                            
                            if (expenseError) throw expenseError;
                            console.log('开销删除成功。');

                            // 5c. 【【 关键：再删“父亲”(Plans) 】】
                            console.log(`正在删除 Plan ID: ${planId} 本身...`);
                            const { error: planError } = await supabaseClient
                                .from('plans')
                                .delete()
                                .eq('id', planId);

                            if (planError) throw planError;
                            console.log('行程删除成功。');

                            // 5d. 刷新列表
                            alert('行程已删除');
                            loadSavedPlans(currentUser.id);

                            // 5e. (可选) 清理主视图
                            if (currentPlanId === planId) {
                                planTitleDisplay.innerText = "";
                                itineraryContent.innerHTML = "请选择一个行程";
                                mapContainer.innerHTML = "地图";
                                mapContainer.classList.add('hidden');
                                expenseSection.classList.add('hidden');
                                currentPlanData = null;
                                currentPlanId = null;
                            }

                        } catch (error) {
                            alert('删除失败: ' + error.message);
                            console.error('Delete error:', error);
                        }
                    });
                    // ⬆️ 【【【【 核心新功能 结束 】】】】

                    savedPlansList.appendChild(li); 
                }); 
            }
            // ⬆️ ⬆️ ⬆️ 【【【【 核心修改 结束 】】】】 ⬆️ ⬆️ ⬆️

            async function loadExpenses(planId) { /* (无修改) */ if (!planId) return; expensesList.innerHTML = "<li>正在加载开销...</li>"; let total = 0; const { data: expenses, error } = await supabaseClient.from('expenses').select('*').eq('plan_id', planId).order('created_at', { ascending: false }); if (error) { alert('加载开销失败: ' + error.message); expensesList.innerHTML = "<li>加载失败</li>"; return; } expensesList.innerHTML = ""; if (!expenses || expenses.length === 0) { expensesList.innerHTML = "<li>暂无开销记录</li>"; } expenses.forEach(expense => { const li = document.createElement('li'); li.innerHTML = `<span>${expense.item}</span> <strong>${expense.amount} 元</strong>`; expensesList.appendChild(li); total += expense.amount; }); expenseTotal.innerText = total; currentManualTotal = total; updateRemaining(); }
            addExpenseBtn.addEventListener('click', async () => { /* (无修改) */ const item = expenseItemInput.value; const amount = expenseAmountInput.value; if (!item || !amount || !currentPlanId) { alert('请填写项目、金额，并确保行程已保存'); return; } try { const { error } = await supabaseClient.from('expenses').insert({ plan_id: currentPlanId, item: item, amount: parseInt(amount) }); if (error) throw error; expenseItemInput.value = ""; expenseAmountInput.value = ""; loadExpenses(currentPlanId); } catch (error) { alert('保存开销失败: ' + error.message); } });
            
            // 这个函数现在只在 loadExpenses 和 generate-btn 逻辑中被调用
            function updateRemaining() { 
                const remaining = currentAiTotal - currentManualTotal; 
                budgetRemaining.innerText = remaining; 
                budgetRemaining.style.color = remaining < 0 ? 'red' : 'green'; 
            }

            // (重构) AI 生成功能
            document.getElementById('generate-btn').addEventListener('click', async () => { 
                const prompt = promptInput.value; 
                itineraryContent.innerHTML = "AI 正在思考中，请稍候..."; 
                planTitleDisplay.innerText = "";
                savePlanBtn.classList.add('hidden');
                expenseSection.classList.add('hidden'); 
                mapContainer.innerHTML = "地图加载中..."; // ⬅️ 重置地图
                mapContainer.classList.add('hidden'); 
                currentPlanId = null; 

                // 【【【 核心修改 2: 重置手动开销 】】】
                currentManualTotal = 0; // 重置手动记账总额
                expenseTotal.innerText = 0; // 更新UI
                expensesList.innerHTML = "<li>暂无开销记录</li>"; // 清理UI

                const response = await fetch('/api/generate-plan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'prompt_from_user': prompt }) }); 
                const data = await response.json(); 
                currentPlanData = data; 
                
                displayPlan(data); // 此时 displayPlan 只会设置 currentAiTotal

                // 【【【 核心修改 3: 在此处调用 updateRemaining 】】】
                // 因为是新计划，我们已经重置了 manual=0，现在可以安全计算了
                updateRemaining(); 
                
                expenseSection.classList.remove('hidden'); 
                myExpensesSection.classList.add('hidden'); 
                if (currentUser) { savePlanBtn.classList.remove('hidden'); } 
            });

            // (重构) 画出“行程+预算”
            function displayPlan(planData) {
                if (!planData) return;
                planTitleDisplay.innerText = planData.plan_title || "未命名行程";
                promptInput.value = planData.plan_title || ""; 
                itineraryContent.innerHTML = ""; 
                if (planData.itinerary && planData.itinerary.length > 0) {
                    planData.itinerary.forEach(day => {
                        let dayHtml = `<h4>第 ${day.day} 天: ${day.day_title || ''}</h4>`;
                        dayHtml += "<ul>";
                        day.activities.forEach(activity => {
                            dayHtml += `<li><strong>${activity.description || 'N_A'}</strong><br><small>地点: ${activity.location_name || 'N_A'}</small></li>`;
                        });
                        dayHtml += "</ul>";
                        itineraryContent.innerHTML += dayHtml;
                    });
                } else { itineraryContent.innerText = "AI 未能生成有效的行程活动。"; }
                const budgetJson = planData.estimated_budget;
                if (!budgetJson || !budgetJson.categories || budgetJson.categories.length === 0) {
                    aiBudgetList.innerHTML = "<li>AI 未提供预算</li>"; aiBudgetTotal.innerText = 0; currentAiTotal = 0;
                } else {
                    aiBudgetList.innerHTML = "";
                    budgetJson.categories.forEach(cat => {
                        const li = document.createElement('li');
                        li.innerHTML = `<span>${cat.item}</span> <strong>${cat.amount} 元</strong>`;
                        aiBudgetList.appendChild(li);
                    });
                    aiBudgetTotal.innerText = budgetJson.total; currentAiTotal = budgetJson.total; 
                }
                
                // 【【【 核心修改 1: 删除此行 】】】
                // updateRemaining(); // ⬅️ 已删除, 移到 generate-btn 逻辑中
                
                mapContainer.classList.remove('hidden');
                
                // (地图函数, 已折叠)
                loadMap(planData); 
            }
            
            // 【【【【 地图函数：串行 + PlaceSearch + 自定义数字Icon 】】】】
            // (此函数保持您上一版本的修改，包含 500ms 延时)
            async function loadMap(planData) {
                const dayColors = ['#E60012', '#F39800', '#FFF100', '#009944', '#0068B7', '#1D2088', '#920783'];
                if (map) { map.destroy(); }
                map = new AMap.Map('map-container', { zoom: 11 }); 
                map.remove(allMarkers); 
                map.remove(allPolylines); 
                allMarkers = [];
                allPolylines = [];
                if (!planData.itinerary || planData.itinerary.length === 0) { mapContainer.innerHTML = "没有可供定位的地点。"; return; }
                AMap.plugin(['AMap.PlaceSearch', 'AMap.Polyline'], () => {
                    console.log('高德 PlaceSearch 和 Polyline 插件加载【成功】');
                    const placeSearch = new AMap.PlaceSearch({ pageSize: 1, pageIndex: 1, city: planData.plan_title || "全国" });
                    let globalActivityCounter = 1; 
                    let mapCentered = false; 
                    (async () => {
                        for (let dayIndex = 0; dayIndex < planData.itinerary.length; dayIndex++) {
                            const day = planData.itinerary[dayIndex];
                            const dayColor = dayColors[dayIndex % dayColors.length];
                            let dayPath = []; 
                            for (let activityIndex = 0; activityIndex < day.activities.length; activityIndex++) {
                                const activity = day.activities[activityIndex];
                                const currentGlobalNum = globalActivityCounter;
                                globalActivityCounter++;
                                const location = activity.location_name;
                                if (!location || location === 'N/A') { console.warn(`[Day ${day.day}] 跳过无效地址`); continue; }
                                console.log(`[${day.day}:${activityIndex + 1}] 正在串行解析: "${location}"...`);
                                const poi = await new Promise((resolve) => {
                                    placeSearch.search(location, (status, result) => {
                                        if (status === 'complete' && result.info === 'OK' && result.poiList.pois.length > 0) {
                                            resolve(result.poiList.pois[0]); 
                                        } else {
                                            console.warn(`[解析失败] 地址: "${location}"`);
                                            console.log("   > 高德返回:", result);
                                            resolve(null); 
                                        }
                                    });
                                });
                                if (poi) {
                                    if (!mapCentered) {
                                        map.setCenter(poi.location); 
                                        map.setZoom(14); 
                                        mapCentered = true;
                                        console.log(`地图已【预定位】到第一个点: ${activity.description}`);
                                    }
                                    const labelContent = `<div class="custom-label" style="border-color: ${dayColor}">Day${day.day}: ${activityIndex + 1}</div>`;
                                    const markerContent = `<div class="custom-marker-icon" style="background-color: ${dayColor}">${currentGlobalNum}</div>`;
                                    const marker = new AMap.Marker({
                                        position: poi.location,
                                        content: markerContent, 
                                        offset: new AMap.Pixel(-15, -15), 
                                        label: { content: labelContent, direction: 'bottom', offset: new AMap.Pixel(0, 0) }
                                    });
                                    let photoHtml = '';
                                    if (poi.photos && poi.photos.length > 0) {
                                        photoHtml = `<img src="${poi.photos[0].url}" alt="${poi.name}">`;
                                    }
                                    const infoWindowContent = `<div class="info-window-content">${photoHtml}<strong>${activity.description}</strong><hr><small>地址: ${poi.address || poi.name}</small></div>`;
                                    marker.on('click', () => {
                                        new AMap.InfoWindow({ content: infoWindowContent, anchor: 'bottom-center' }).open(map, marker.getPosition());
                                    });
                                    map.add(marker); 
                                    allMarkers.push(marker); 
                                    dayPath.push(poi.location); 
                                }
                                
                                // (QPS 限制)
                                console.log('...等待 500ms...');
                                await new Promise(resolve => setTimeout(resolve, 500));
                            } 
                            if (dayPath.length > 1) {
                                const polyline = new AMap.Polyline({
                                    path: dayPath,
                                    strokeColor: dayColor, 
                                    strokeOpacity: 0.8,
                                    strokeWeight: 6,
                                });
                                map.add(polyline);
                                allPolylines.push(polyline);
                            }
                        } 
                        console.log('【【【 地图串行渲染完毕 】】】');
                        if (allMarkers.length > 0) {
                            map.setFitView(); 
                            console.log('地图已【最终缩放】以适应所有点。');
                        }
                    })(); 
                });
            }
            
            // ... (录音 JS, 已折叠) ...
            let mediaRecorder; let audioChunks = []; let isRecording = false;
            recordBtn.addEventListener('mousedown', async () => { if (isRecording) return; try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = (event) => { audioChunks.push(event.data); }; mediaRecorder.onstop = sendAudioData; audioChunks = []; mediaRecorder.start(); isRecording = true; recordStatus.innerText = "录音中... 松开停止"; recordBtn.classList.add('recording'); } catch (err) { alert('获取麦克风失败: ' + err.message); } });
            recordBtn.addEventListener('mouseup', () => { if (!isRecording) return; mediaRecorder.stop(); isRecording = false; recordStatus.innerText = "后端转码并识别中..."; recordBtn.classList.remove('recording'); mediaRecorder.stream.getTracks().forEach(track => track.stop()); });
            async function sendAudioData() { const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); const formData = new FormData(); formData.append('audio_data', audioBlob, 'recording.webm'); try { const response = await fetch('/api/transcribe', { method: 'POST', body: formData }); if (!response.ok) throw new Error(`服务器错误: ${response.statusText}`); const data = await response.json(); if (data.text) { promptInput.value = data.text; recordStatus.innerText = "识别成功！"; } else { throw new Error(data.error || '未返回文本'); } } catch (err) { alert('语音识别失败: ' + err.message); recordStatus.innerText = "识别失败"; } }

        });
    </script>

</body> 
</html>